﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace Sharp.Interprocess
{
    public class HashsetBucketCollection<T> : IDisposable
    {
        private const string COLLECTION_MAP_NAME_SUFFIX = "_HashsetBuckets";
        #region Private Members

        private SharedCollection<HashsetBucketInfo> m_HashBucketStorage;        

        #endregion

        #region Constructor(s)

        public HashsetBucketCollection(string collectionMapName)
        {
            m_HashBucketStorage = new SharedCollection<HashsetBucketInfo>(collectionMapName + COLLECTION_MAP_NAME_SUFFIX, 0, 
                                                                          HashsetBucketInfoEqualityComparer.Instance);
        }

        #endregion

        #region Public Members

        public IEnumerable<HashsetBucketInfo> StoredHashBucketEntries
        {
            get
            {
                return m_HashBucketStorage.ToList();
            }
        }

        public void ClearCollection()
        {
            m_HashBucketStorage.Clear();
        }

        public void AddItemToBucket(T item,int itemOffset)
        {
            int itemHashCode = item.GetHashCode();
            var existingBucket = m_HashBucketStorage.FirstOrDefault(bucketInfo => bucketInfo.BucketKey == itemHashCode);
            if (existingBucket == null)
            {
                var newBucketInfo = new HashsetBucketInfo() { BucketKey = itemHashCode };
                newBucketInfo.BucketMemberOffsets.Add(itemOffset);
                m_HashBucketStorage.Add(newBucketInfo);
            }
            else
            {
                existingBucket.BucketMemberOffsets.Add(itemOffset);
                m_HashBucketStorage.AddOrUpdate(existingBucket);
            }
        }

        public void RemoveItemFromBucket(T item,int itemOffset)
        {
            int itemHashCode = item.GetHashCode();
            var existingBucket = m_HashBucketStorage.FirstOrDefault(bucketInfo => bucketInfo.BucketKey == itemHashCode);
            if (existingBucket != null && existingBucket.BucketMemberOffsets.Any(offset => offset == itemOffset))
            {
                existingBucket.BucketMemberOffsets.Remove(itemOffset);
                m_HashBucketStorage.AddOrUpdate(existingBucket);
            }
        }

        public IEnumerable<int> GetItemOffsetsFromMultipleBuckets(IEnumerable<int> bucketHashcodeKeys)
        {
            var relevantBucketsInfo = m_HashBucketStorage.Where(bucketInfo => bucketHashcodeKeys.Contains(bucketInfo.BucketKey));
            if (relevantBucketsInfo != null)
            {
                return relevantBucketsInfo.SelectMany(bucketInfo => bucketInfo.BucketMemberOffsets);
            }
            else
            {
                return new List<int>();
            }
        }

        public IEnumerable<int> GetItemOffsetsFromBucket(int bucketHashcodeKey)
        {
            var relevantBucketInfo = m_HashBucketStorage.FirstOrDefault(bucketInfo => bucketInfo.BucketKey == bucketHashcodeKey);
            if (relevantBucketInfo != null)
            {
                return relevantBucketInfo.BucketMemberOffsets;
            }
            else
            {
                return new List<int>();
            }            
        }

        #endregion

        #region IDisposable Implementation
        public void Dispose()
        {
            m_HashBucketStorage.Dispose();
        }
        #endregion

        #region Helper Class Definitions
        private class HashsetBucketInfoEqualityComparer : IEqualityComparer<HashsetBucketInfo>
        {
            private static HashsetBucketInfoEqualityComparer m_Instance;

            public static HashsetBucketInfoEqualityComparer Instance
            {
                get
                {
                    if (m_Instance == null)
                    {
                        m_Instance = new HashsetBucketInfoEqualityComparer();
                    }

                    return m_Instance;
                }
            }

            private HashsetBucketInfoEqualityComparer() { }

            public bool Equals(HashsetBucketInfo x, HashsetBucketInfo y)
            {
                return x.BucketKey == y.BucketKey;
            }

            public int GetHashCode(HashsetBucketInfo hashsetBucketInfo)
            {
                int hashCode = 17;
                hashCode = 23 * hashCode + hashsetBucketInfo.BucketKey.GetHashCode();
                hashCode = 23 * hashCode + hashsetBucketInfo.BucketMemberOffsets.GetHashCode();
                return hashCode;
            }
        }
        #endregion
    }
}
